<template>
  <div>
    <div  class="upload" @click="selectFile">
      <div class="upload-text">
          <img src="~@/assets/images/gzq/add.png" alt="">
      </div>
      <input type="file" @change="fileOnChange" id="fileInput" :accept="accept" style="display: none"></input>
    </div>
    <div class="fileExport" v-if="isShowSc">
      <img src="@/assets/images/gzq/close3.png" alt="" @click="isShowSc = false">
      <div v-for="(item,index) in fileList" :key="index">
        <div style="height: 30px">
          <span style="font-size: 14px;">{{ item.file.name }}</span>
          <span style="margin-left:10px;font-size: 14px;">{{ item.size }}M</span>
          <span style="margin-left:20px;color: blue">{{ item.uploadStateTxt }}</span>
          <el-button v-show="item.loadingShow" :loading="true" type="text"></el-button>
        </div>
        <div class="projectUpload">
          <span>{{ item.uploadStateTxt}}</span>
          <el-progress style="width: 360px" :text-inside="true" :stroke-width="16"
                         :percentage="item.uploadProgress"></el-progress>
          <div>
            <el-button v-show="item.uploadBtnShow" type="primary" size="small" icon="el-icon-upload2" circle
                       @click="uploadFile(item)"></el-button>
            <el-button type="danger" size="small" icon="el-icon-delete" circle @click="deleteFile(item)"></el-button>
          </div>
        </div>
      </div>
    </div>
  </div>
</template>
<script>
import SparkMD5 from 'spark-md5'
import {uploadChunk, mergeChunk, deleteChunks, deleteFile} from "@/api/upload/upload";

export default {
  name: 'ChunkUpload',
  props: {
    accept: {
      type: String,
      default: null,
    },
    disabled: Boolean,
    limit: {
      type: Number,
      default: 3,
    },
    chunkSize: {
      type: Number,
      default: 5 * 1024 * 1024,
    },
    thread: {
      type: Number,
      default: 1,
    },
    beforeUpload:  {
      type: Function,
      default: ()=>{},
    },
    beforeRemove:  {
      type: Function,
      default: ()=>{},
    },
    onRemove: {
      type: Function,
      default: ()=>{},
    },
    onChange:  {
      type: Function,
      default: ()=>{},
    },
    onSuccess: {
      type: Function,
      default: ()=>{},
    },
    onProgress: {
      type: Function,
      default: ()=>{},
    },
    onError: {
      type: Function,
      default: ()=>{},
    },
  },
  data() {
    return {
      fileList: [],
      isShowSc:false,
    }
  },
  methods: {
    selectFile() {
      document.querySelector('#fileInput').click()

    },
    fileOnChange() {
      let file = document.querySelector('#fileInput').files[0];
      // 检查是否已经选中
      // if (this.checkFileByName(file.name) !== -1) {
      //   this.clearFileInput();
      //   this.msgInfo("文件已存在!")

      //   return
      // }
      this.onChange(file);
      let fileInfo = {
        file: file,
        name: file.name,
        length:file.size,
        size: (file.size / (1024 * 1024)).toFixed(2),
        contentType:file.type,
        chunks: [],
        chunkTotal: Math.ceil(file.size / this.chunkSize),
        md5: undefined,
        uploadProgress: 0,
        hashProgress: 0,
        uploadStateTxt: "计算分片中...",
        uploadState: 1,  // 1  计算分片 2 分割 3 上传  4 合并 5 上传完成
        loadingShow: true,
        uploadBtnShow: false,
        ifUploadFinish: false,
      }
      this.fileList.push(fileInfo)
      this.isShowSc = true
      this.calculateFileMd5(fileInfo, this.chunkSize)
    },
    // 计算分片
    calculateFileMd5(fileInfo, chunkSize) {
      let blobSlice = File.prototype.mozSlice || File.prototype.webkitSlice || File.prototype.slice;
      let fileReader = new FileReader();
      let chunkTotalNum = fileInfo.chunkTotal;
      let currentChunkNum = 0;
      let spark = new SparkMD5();
      fileReader.onload = (e) => {
        spark.appendBinary(e.target.result);
        currentChunkNum++;
        if (currentChunkNum < chunkTotalNum) {
          loadNext();
        } else {
          const md5 = spark.end();
          // // 检查是否已经选中
          // if (this.checkFileByMd5(md5) !== -1) {
          //   this.clearFileInput();
          //   this.fileList.splice(this.fileList.findIndex(item => item.md5 === fileInfo.md5), 1)
          //   this.msgInfo("文件已存在!")
          //   return
          // }
          fileInfo.md5 = md5;
          fileInfo.uploadStateTxt = undefined;
          fileInfo.loadingShow = false;
          fileInfo.uploadBtnShow = false;
          this.calculateFileMd5Success(fileInfo);
        }
      };

      function loadNext() {
        const start = currentChunkNum * chunkSize;
        const end = start + chunkSize >= fileInfo.file.size ? fileInfo.file.size : start + chunkSize;
        fileReader.readAsBinaryString(blobSlice.call(fileInfo.file, start, end));
      };
      loadNext();
    },
    // 计算分片完成
    calculateFileMd5Success(fileInfo) {
      this.clearFileInput();
      fileInfo.chunks = this.slicingFile(fileInfo, this.chunkSize);
      if (fileInfo.chunks.length === fileInfo.chunkTotal) {
        fileInfo.uploadStateTxt = undefined;
        fileInfo.loadingShow = false;
        fileInfo.uploadBtnShow = true;
      }
    },
    // 上传文件
    uploadFile(fileInfo) {
      this.beforeUpload();
      fileInfo.uploadStateTxt = "文件上传中，请稍后...";
      fileInfo.uploadState = 3;
      fileInfo.loadingShow = true;
      fileInfo.uploadBtnShow = false;
      const result = [];
      let maxThread = this.thread;
      let reqNum = 0; // 当前有多少ajax正在执行
      fileInfo.interval = setInterval(() => {
        if (reqNum < maxThread && fileInfo.chunks.length > 0 && result.length < fileInfo.chunkTotal) { // 只要有位置就发请求
          reqNum++;
          const chunk = fileInfo.chunks.shift()
          this.uploadChunk(chunk, fileInfo.md5).then(res => {
            if (res.code === 200) {
              result.push(res)
              fileInfo.uploadProgress = Math.round(((chunk.chunkIndex + 1) / fileInfo.chunkTotal) * 100);
            } else {
              clearInterval(fileInfo.interval);
            }
          }).catch(err => {
            fileInfo.chunks.push(chunk)
          }).finally(() => {
            this.onProgress(fileInfo);
            if (result.length === fileInfo.chunkTotal) {
              // 分片上传完成
              fileInfo.uploadStateTxt = undefined;
              fileInfo.loadingShow = false;
              fileInfo.chunks = [];
              clearInterval(fileInfo.interval);
              this.mergeChunk(fileInfo)
            }
            reqNum--;
          })
        }
      }, Math.random() * 100)
    },
    // 清空 fileInput
    clearFileInput() {
      document.querySelector('#fileInput').value = null;
    },
    // 删除文件
    deleteFile(fileInfo) {
      clearInterval(fileInfo.interval);
      this.clearFileInput();
      this.fileList.splice(this.fileList.findIndex(item => item.md5 === fileInfo.md5), 1)
      if (fileInfo.ifUploadFinish) {
        deleteFile(fileInfo.md5, fileInfo.name).then(res => {
          if (res.code === 200) {
            this.msgSuccess("删除成功!")
            this.isShowSc = false
          }
        })
      } else {
        if (fileInfo.uploadState >= 3) {
          let interval = setInterval(() => {
            deleteChunks(fileInfo.md5).then(res => {
              if (res.code === 200) {
                clearInterval(interval)
                this.msgSuccess("删除成功!")
                this.isShowSc = false
              }
            })
          }, 1000)
        }
      }
    },
    // 切割文件
    slicingFile(fileInfo, chunkSize) {
      fileInfo.uploadStateTxt = "正在切割...";
      fileInfo.uploadState = 2;
      fileInfo.loadingShow = true;
      fileInfo.uploadBtnShow = false;
      const fileChunkList = [];
      let count = 0;
      let chunkIndex = 0;
      while (count < fileInfo.file.size) {
        fileChunkList.push({
          file: fileInfo.file.slice(count, count + chunkSize),
          chunkIndex: chunkIndex,
          chunkTotal: fileInfo.chunkTotal
        });
        count += chunkSize;
        chunkIndex += 1;
      }
      return fileChunkList;
    },
    // 上传分片
    uploadChunk(chunk, md5) {
      return uploadChunk(chunk.file, chunk.chunkIndex, chunk.chunkTotal, md5)
    },
    // 合并分片
    mergeChunk(fileInfo) {
      fileInfo.uploadStateTxt = "正在合并分片...";
      fileInfo.uploadState = 4;
      fileInfo.loadingShow = true;
      fileInfo.uploadBtnShow = false;
      return mergeChunk(fileInfo.md5,fileInfo.length,fileInfo.contentType,fileInfo.chunkTotal,fileInfo.name).then(res => {
        if (res.code === 200) {
          fileInfo.uploadStateTxt = "上传完成！";
          fileInfo.uploadState = 5;
          fileInfo.ifUploadFinish = true;
          fileInfo.loadingShow = false;
          fileInfo.uploadBtnShow = false;
          this.onSuccess(res.data);
        }
        setTimeout(()=>{
          deleteFile(fileInfo.md5)
          this.isShowSc = false
          this.fileList.pop()
        },100)
      })
    },
    checkFileByName(fileName) {
      return this.fileList.findIndex(item => item.name === fileName)
    },
    checkFileByMd5(md5) {
      return this.fileList.findIndex(item => item.md5 === md5)
    },
  }
}
</script>

<style scoped lang="scss">
.fileExport{
  display: flex;
  align-items: center;
  // flex-direction: column;
  position: fixed;
  bottom: 10%;
  left: 30%;
  width: 800px;
  // height: 113px;
  border: 1px solid #edf0f2;
  background: #ffffff;
  box-shadow: 0px 5px 20px #0000001a;
  border-radius: 50px;
  padding: 19px 28px;
  box-sizing: border-box;
  >img{
    position: absolute;
    top: -15px;
    right: -16px;
    width: 24px;
    height: 24px;
    cursor: pointer;

  }
  >div{
    display: flex;
    align-items: center;
    justify-content: space-between;
    width: 100%;

  }
  .projectUpload{
    display: flex;
    align-items: center;
    justify-content: space-between;
    width: 100%;
    .el-progress{
      flex: 1;
      margin: 0 10px;
    }
  }
}
.upload {
  display: flex;
  justify-content: center;
  align-items: center;
  width: 148px;
  height: 148px;
  background-color: #fbfdff
}
.upload-text{
  width: 100%;
  height: 100%;
  display: flex;
  flex-flow: column;
  align-items: center;
  justify-content: center;
    >img{
      width: 56px;
      height: 56px;
    }
    >span{
      color: rgba(65, 107, 170, 1);
      font-family: OPPOSans;
      font-size: 14px;
      line-height: 22px;
    }
}
</style>
